/**
 * Octopus, an XMMS2 client.
 * Copyright (C) 2014 Octopus Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

namespace Octopus {
	public class Collectionlist : Object, IMedialibQuery {
		private Gee.List<CollectionGroupedByAlbum> _collectionlist;
		private Gee.List<Playlist> _playlistlist;
		private Gee.List<ICollection> _sortedlist;
		private bool _collectionlist_load_flag = false;
		private bool _playlistlist_load_flag = false;

		public signal void insert (IMedialibInfo e, int pos);

		CompareFunc<ICollection> medialib_compare = (a, b) => {
			return a.get_compare_string ().collate (b.get_compare_string ());
		};

		public Collectionlist () {
			Object ();
		}

		construct {
			_collectionlist = new Gee.ArrayList<CollectionGroupedByAlbum> ();
			_playlistlist   = new Gee.ArrayList<Playlist> ();
			_sortedlist     = new Gee.ArrayList<ICollection> ();

			Global.get ().get_xmms_client ().get_xmms ().broadcast_collection_changed ().notifier_set (on_collection_changed);
		}

		private void collectionlist_loaded () {
			_collectionlist_load_flag = true;
			_sortedlist.add_all (_collectionlist);
			if (_playlistlist_load_flag) {
				_sortedlist.sort (medialib_compare);
				loaded ();
			}
		}

		private void playlistlist_loaded () {
			_playlistlist_load_flag = true;
			_sortedlist.add_all (_playlistlist);
			if (_collectionlist_load_flag) {
				_sortedlist.sort (medialib_compare);
				loaded ();
			}
		}

		public void load () {
			_collectionlist.clear ();
			_playlistlist.clear ();
			_sortedlist.clear ();
			_collectionlist_load_flag = false;
			_playlistlist_load_flag = false;

			Global.get ().get_xmms_client ().get_xmms ().coll_list (Xmms.COLLECTION_NS_COLLECTIONS).notifier_set (on_list_collections);

			Global.get ().get_xmms_client ().get_xmms ().coll_list (Xmms.COLLECTION_NS_PLAYLISTS).notifier_set (on_list_playlists);
		}

		private bool on_list_collections (Xmms.Value values) {
			unowned Xmms.ListIter list_iter;

			values.get_list_iter(out list_iter);

			if (!list_iter.valid()) collectionlist_loaded (); //empty iterator; no query result

			for (list_iter.first(); list_iter.valid(); list_iter.next()) {
				unowned Xmms.Value entry;
				string? name = null;

				if (!(list_iter.entry(out entry) && entry.get_string (out name)))
					continue;

				// Ignore hidden collections
				if (name[0] == '_')
					continue;

				CollectionGroupedByAlbum e = new CollectionGroupedByAlbum (name);
				_collectionlist.add (e);
			}

			collectionlist_loaded ();

			return true;
		}

		private bool on_list_playlists (Xmms.Value values) {
			unowned Xmms.ListIter list_iter;

			values.get_list_iter(out list_iter);

			if (!list_iter.valid()) playlistlist_loaded (); //empty iterator; no query result

			for (list_iter.first(); list_iter.valid(); list_iter.next()) {
				unowned Xmms.Value entry;
				string? name = null;

				if (!(list_iter.entry(out entry) && entry.get_string (out name)))
					continue;

				// Ignore hidden collections
				if (name[0] == '_')
					continue;

				Playlist e = new Playlist (name, true);
				_playlistlist.add (e);
			}

			playlistlist_loaded ();

			return true;
		}

		public void request_data () {
			foreach (var i in _sortedlist) {
				add ((IMedialibInfo) i);  //emit signal
			}
			add_finished ();
		}

		public int search_string_position (string search) {
			int i;
			for (i = 0; i < _sortedlist.size; i++) {
				if (search.down () <= _sortedlist[i].get_compare_string ().down ()) break;
			}
			//no match retrun -1
			if (i == _sortedlist.size) i = -1; 

			return i;
		}

		private bool on_collection_changed(Xmms.Value val) {
			string name, newname, ns;
			int change;
			bool tmp;

			tmp = val.dict_entry_get_string("name", out name);
			tmp = val.dict_entry_get_string("namespace", out ns);
			tmp = val.dict_entry_get_int("type", out change);

			switch (change) {
				case Xmms.CollectionChanged.ADD:
					collection_add (name, ns);
					break;
				case Xmms.CollectionChanged.UPDATE:
					break;
				case Xmms.CollectionChanged.RENAME:
					if (val.dict_entry_get_string("newname", out newname)) {
						collection_rename(name, newname, ns);
					}
					break;
				case Xmms.CollectionChanged.REMOVE:
					collection_remove(name, ns);
					break;
				default:
					break;
			}

			return true;
		}

		private void collection_add (string name, string ns) {
			ICollection sortedlist_entry = null;
			if (ns == Xmms.COLLECTION_NS_COLLECTIONS) {
				CollectionGroupedByAlbum collection = new CollectionGroupedByAlbum (name);
				_collectionlist.add (collection);
				sortedlist_entry = collection;
			}
			if (ns == Xmms.COLLECTION_NS_PLAYLISTS) {
				Playlist playlist = new Playlist (name, true);
				_playlistlist.add (playlist);
				sortedlist_entry = playlist;
			}
			_sortedlist.add (sortedlist_entry);
			_sortedlist.sort (medialib_compare);
			insert ((IMedialibInfo) sortedlist_entry, _sortedlist.index_of (sortedlist_entry)); //emit signal
		}

		private void collection_rename (string name, string newname, string ns) {
			collection_remove (name, ns);
			collection_add (newname, ns);
		}

		private void collection_remove (string name, string ns) {
			ICollection e = find_by_name (name, ns);
			if (e != null) {
				e.remove ();
				if (ns == Xmms.COLLECTION_NS_COLLECTIONS) {
					_collectionlist.remove ((CollectionGroupedByAlbum)e);
				}
				if (ns == Xmms.COLLECTION_NS_PLAYLISTS) {
					_playlistlist.remove ((Playlist)e);
				}
				_sortedlist.remove (e);
			}
		}

		private ICollection? find_by_name (string name, string ns) {
			//assume name = compareString
			for (int i = 0; i < _sortedlist.size; i++) {
				if (   (_sortedlist[i].get_compare_string () == name)
				    && (_sortedlist[i].get_namespace () == ns))
					return _sortedlist[i];
			}
			return null;
		}
	}
}
